package untils;

import org.apache.poi.ss.usermodel.*;

import java.lang.reflect.Field;
import java.text.NumberFormat;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @author ghp
 * @date 2023/1/25
 * @title 解析Excel表格的工具类
 * @description
 */
public class ExcelUtil {

    /**
     * 读取工作簿中的数据，将数据先封装成一个实体类，然后存入List集合中
     * @param workbook
     * @param clazz
     * @return
     * @param <T>
     * @throws Exception
     */
    public static <T> List<T> readExcel(Workbook workbook, Class<T> clazz) throws Exception{
        List<T> result =  new ArrayList<>();
        // 获取要操作的表
        Sheet sheet = workbook.getSheetAt(0);
        // 获取表的所有行数
        int rowNum = sheet.getPhysicalNumberOfRows();
        // 获取标题数据（隐藏行，也就是第二行）
        Row row = sheet.getRow(1);

        // 遍历row中数据，将这些数据存储到Map集合中，用于对应实体类（key中存储实体类的属性，也就是隐藏行的值）
        List<String> key = new ArrayList<>();
        for (Cell cell : row) {
            if (cell != null) {
//                String cellValue = cell.getStringCellValue();
                // 调我们自己封装的方法，更加稳妥
                String cellValue = getCellValue(cell);
                key.add(cellValue);
            }
        }

        // 获取正式数据（表中第二行以后的数据）
        for (int i = 2; i < rowNum; i++) {
            row = sheet.getRow(i);
            if (row != null) {
                int j = 0;
                // key用来存属性名，value用来存值，好于实体类对应
                Map<String, String> excelMap = new HashMap<>();
                for (Cell cell : row) {
                    if (cell != null) {
                        // 把所有单元格中的数据设置为String
                        String cellValue = getCellValue(cell);
                        if(cellValue != null && cellValue != ""){
                            excelMap.put(key.get(j), cellValue);
                            j++;
                        }
                    }
                }
                // 创建对应实体类的类型，并且把读取到的数据转换成实体类
                T t = mapToEntity(excelMap, clazz);
                result.add(t);
            }
        }
        return result;
    }

    /**
     * 去掉后面的数值后面的 .0
     */
    public static NumberFormat nf = NumberFormat.getNumberInstance();
    static {
        nf.setGroupingUsed(false);
    }

    /**
     * 获取单元格中的数据
     * @param cell
     * @return
     */
    private static String getCellValue(Cell cell){
        String cellValue = null;
        if (cell != null) {
            CellType cellType = cell.getCellType();
            // 根据不同的数据类型读取数据
            switch (cellType) {
                case STRING: // 字符串类型
                    cellValue = cell.getStringCellValue();
                    break;
                case NUMERIC: // 数值类型
                    // 需要先判断是否是日期类型，因为使用字符串的读取方式读取日期，会有问题
                    if (DateUtil.isCellDateFormatted(cell)) {
                        // 如果当前是日期类型，则使用日期数据的读取方式
                        Date cellDate = cell.getDateCellValue();
                        // 将日期进行格式转发，并转成字符串
                        cellValue = new SimpleDateFormat("yyyy-MM-dd").format(cellDate);
                    } else {
                        // 如果当前是日期类型，则使用数值数据的读取方式
//                        cellValue = cell.toString();
                        // 格式化数值，去掉后面的 .0
                        cellValue  = nf.format(cell.getNumericCellValue());
                    }
                    break;
                case BLANK: // 空字符串
                    System.out.println("当前单元格为空");
                    break;
                case BOOLEAN: // 布尔类型
                    cellValue = String.valueOf(cell.getBooleanCellValue());
                    break;
                case ERROR: // 错误类型
                    System.out.println("格式错误");
                    break;
            }
        }
        return cellValue;
    }

    /**
     * 将Map转成对应的实体类
     * @param map
     * @param entity
     * @return
     * @param <T>
     * @throws Exception
     */
    private static <T> T mapToEntity(Map<String, String> map, Class<T> entity) throws Exception {
        T t;
        t = entity.newInstance();
        for (Field field : entity.getDeclaredFields()) {
            if (map.containsKey(field.getName())){
                // 暴力反射，突破访问权限
                field.setAccessible(true);
                // 如果当前map中有该属性，就先获取它的值
                String value = map.get(field.getName());
                // 获取实体类的类型（是类型对应的全类名）
                String type = field.getGenericType().toString();
                if(value != null){
                    if(type.equals("class java.lang.String")){
                        // 如果当前属性是String类型，就将对应的字段直接设置为当前值
                        field.set(t, value);
                    } else if (type.equals("class java.lang.Double")) {
                        // 如果当前的属性是Double类型，需要将它转换成Double再存（当前的值都是String类型的）
                        field.set(t, Double.parseDouble(value));
                    } else if (type.equals("class java.lang.Integer")) {
                        // 如果当前的属性是Integer类型，需要将它转换成Integer再存（当前的值都是String类型的）
                        field.set(t, Integer.parseInt(value));
                    } else if (type.equals("class java.util.Date")) {
                        // 如果当前的属性是Date类型，需要将它转换成Date再存(注意要格式化一下)
                        Date date =  new SimpleDateFormat("yyyy-MM-dd").parse(value);
                        field.set(t, date);
                    }
                }
                // 将当前的安全警察设置为false
                field.setAccessible(false);
            }
        }
        return t;
    }
}
